home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Games Extra 1996 September
/
Amiga Games Extra CD-ROM 9-1996.iso
/
userbox
/
publicdomain
/
vim-4.2
/
src
/
help.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-09
|
7KB
|
302 lines
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* help.c: open a read-only window on the vim_help.txt file
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
void
do_help(arg)
char_u *arg;
{
char_u *fnamep;
FILE *helpfd; /* file descriptor of help file */
int n;
WIN *wp;
int num_matches;
char_u **matches;
int need_free = FALSE;
/*
* If an argument is given, check if there is a match for it.
*/
if (*arg != NUL)
{
n = find_help_tags(arg, &num_matches, &matches);
if (num_matches == 0 || n == FAIL)
{
EMSG2("Sorry, no help for %s", arg);
return;
}
/* The first file is the best match */
arg = strsave(matches[0]);
need_free = TRUE;
FreeWild(num_matches, matches);
}
/*
* If there is already a help window open, use that one.
*/
if (!curwin->w_buffer->b_help)
{
for (wp = firstwin; wp != NULL; wp = wp->w_next)
if (wp->w_buffer != NULL && wp->w_buffer->b_help)
break;
if (wp != NULL && wp->w_buffer->b_nwindows > 0)
win_enter(wp, TRUE);
else
{
/*
* There is no help buffer yet.
* Try to open the file specified by the "helpfile" option.
*/
fnamep = p_hf;
if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
{
#if defined(MSDOS)
/*
* for MSDOS: try the DOS search path
*/
fnamep = searchpath("vim_help.txt");
if (fnamep == NULL ||
(helpfd = fopen((char *)fnamep, READBIN)) == NULL)
{
smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
goto erret;
}
#else
smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
goto erret;
#endif
}
fclose(helpfd);
if (win_split(0, FALSE) == FAIL)
goto erret;
if (curwin->w_height < p_hh)
win_setheight((int)p_hh);
#ifdef RIGHTLEFT
curwin->w_p_rl = 0; /* help window is left-to-right */
#endif
curwin->w_p_nu = 0; /* no line numbers */
/*
* open help file (do_ecmd() will set b_help flag, readfile() will
* set b_p_ro flag)
*/
(void)do_ecmd(0, fnamep, NULL, NULL, TRUE, (linenr_t)0, TRUE);
/* save the values of the options we change */
vim_free(help_save_isk);
help_save_isk = strsave(curbuf->b_p_isk);
help_save_ts = curbuf->b_p_ts;
/* accept all chars for keywords, except ' ', '*', '"', '|' */
set_string_option((char_u *)"isk", -1,
(char_u *)"!-~,^*,^|,^\"", TRUE);
curbuf->b_p_ts = 8;
check_buf_options(curbuf);
(void)init_chartab(); /* needed because 'isk' changed */
}
}
restart_edit = 0; /* don't want insert mode in help file */
stuffReadbuff((char_u *)":ta ");
if (arg != NULL && *arg != NUL)
stuffReadbuff(arg);
else
stuffReadbuff((char_u *)"vim_help.txt"); /* go to the index */
stuffcharReadbuff('\n');
erret:
if (need_free)
vim_free(arg);
}
/*
* Return a heuristic indicating how well the given string matches. The
* smaller the number, the better the match. This is the order of priorities,
* from best match to worst match:
* - Match with least alpha-numeric characters is better.
* - Match with least total characters is better.
* - Match towards the start is better.
* Assumption is made that the matched_string passed has already been found to
* match some string for which help is requested. webb.
*/
int
help_heuristic(matched_string, offset)
char_u *matched_string;
int offset; /* offset for match */
{
int num_letters;
char_u *p;
num_letters = 0;
for (p = matched_string; *p; p++)
if (isalnum(*p))
num_letters++;
/*
* Multiply the number of letters by 100 to give it a much bigger
* weighting than the number of characters.
* If the match starts in the middle of a word, add 10000 to put it
* somewhere in the last half.
* If the match is more than 2 chars from the start, multiply by 200 to
* put it after matches at the start.
*/
if (isalnum(matched_string[offset]) && offset > 0 &&
isalnum(matched_string[offset - 1]))
offset += 10000;
else if (offset > 2)
offset *= 200;
return (int)(100 * num_letters + STRLEN(matched_string) + offset);
}
static int help_compare __ARGS((const void *s1, const void *s2));
/*
* Compare functions for qsort() below, that checks the help heuristics number
* that has been put after the tagname by find_tags().
*/
static int
help_compare(s1, s2)
const void *s1;
const void *s2;
{
char *p1;
char *p2;
p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
return strcmp(p1, p2);
}
/*
* Find all help tags matching "arg", sort them and return in matches[], with
* the number of matches in num_matches.
* We try first with case, and then ignoring case. Then we try to choose the
* "best" match from the ones found.
*/
int
find_help_tags(arg, num_matches, matches)
char_u *arg;
int *num_matches;
char_u ***matches;
{
char_u *s, *d;
regexp *prog;
int attempt;
int retval = FAIL;
reg_magic = p_magic;
d = IObuff; /* assume IObuff is long enough! */
/*
* Replace "|" with "bar", """ with "quote" and "*" with "star" to
* match the name of the tags for these commands.
* Replace "*" with ".*" and "?" with "." to match command line
* completion.
* Insert a backslash before '~', '$' and '.' to avoid their
* special meaning.
* Replace "^x" by "CTRL-X". Don't do this for "^_" to make
* ":help i_^_CTRL-D" work.
* If tag starts with ', toss everything after a second '. Fixes
* CTRL-] on 'option'. (would include the trailing '.').
*/
if (STRCMP(arg, "*") == 0 || STRCMP(arg, "[*") == 0 ||
STRCMP(arg, "]*") == 0)
{
if (*arg != '*')
*d++ = *arg;
STRCPY(d, "star");
d += 4;
}
else
{
for (s = arg; *s; ++s)
{
if (d - IObuff > IOSIZE - 10) /* getting too long!? */
break;
switch (*s)
{
case '|': STRCPY(d, "bar");
d += 3;
continue;
case '\"': STRCPY(d, "quote");
d += 5;
continue;
case '*': *d++ = '.';
break;
/* "?", ":?" and "?<CR>" are real tags */
case '?': if (arg[1] == NUL ||
STRCMP(arg, ":?") == 0 ||
STRCMP(arg, "?<CR>") == 0)
break;
*d++ = '.';
continue;
case '$':
case '.':
case '~': *d++ = '\\';
break;
}
if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_')) /* ^x */
{
STRCPY(d, "CTRL-");
d += 5;
if (*s < ' ')
{
*d++ = *s + '@';
continue;
}
++s;
}
else if (*s == '^') /* "^" or "CTRL-^" or "^_" */
*d++ = '\\';
*d++ = *s;
if (*s == '\'' && s > arg && *arg == '\'')
break;
}
}
*d = NUL;
reg_ic = FALSE;
prog = vim_regcomp(IObuff);
if (prog == NULL)
return FAIL;
/* First try to match with case, then without */
for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
{
*matches = (char_u **)"";
*num_matches = 0;
retval = find_tags(NULL, prog, num_matches, matches, TRUE);
if (retval == FAIL || *num_matches)
break;
}
vim_free(prog);
#ifdef HAVE_QSORT
/*
* Sort the matches found on the heuristic number that is after the
* tag name. If there is no qsort, the output will be messy!
*/
qsort((void *)*matches, (size_t)*num_matches,
sizeof(char_u *), help_compare);
#endif
return OK;
}